#include <stdint.h>
#include <stdfunc.h>
#include <stdio.h>
#include <text_video.h>
#include <memory.h>
#include <multiboot.h>
#include <cpuid.h>
#include <pci.h>
#include <pci_database.h>
#include <mp_tables.h>
#include <smbios.h>
#include <acpi.h>
#include <task.h>
#include <keyboard.h>
#include <shell.h>

static void help(void)
{
    uint32_t x;
    
    GUTS();
    
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");

    printf("available commands:\n");
    
    for(x = 0; x < command_pos; x++)
    {
        printf(" : %s\n", command_name_buf[x]);
    }
    
    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void system_info(void)
{
    uint32_t x, c;
    
    GUTS();
    
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");
    
    printf("kernel info:\n");
    printf(" : E/OS v%s \n", _SYS_VER);
    printf(" : build time/date = %s / %s\n", __DATE__, __TIME__);
    printf(" : GCC version = %u.%u.%u\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
    printf(" : NASM version = %s\n", __NASM_VER__);
    printf(" : reserved kernel space = %uKB\n", ((uint32_t)&kernel_end - (uint32_t)&kernel_start)/1024);

    printf("\nBIOS info:\n");
    if(sm_bios.vendor_s){
    printf(" : vendor = %s\n", sm_bios.vendor_s);}
    if(sm_bios.release_date_s){
    printf(" : release date = %s\n", sm_bios.release_date_s);}
    printf(" : ROM size = %uKB\n", ((0x10000 * (sm_bios.rom_size + 1)) / 1024));

    printf("\nSMBIOS info:\n");
    if(smbios_present)
    {
        printf(" : version = %u.%u\n", sm_eps.major_version, sm_eps.minor_version);
        printf(" : first structure address = %x\n", sm_eps.struct_table_address); 
        printf(" : structure count = %u\n", sm_structure_count);
    }
    
    printf("\nACPI info:\n");
    if(acpi_present)
    {
        printf(" : version = %u\n", acpi_rsdp.revision+1);
        printf(" : OEM id = \"%s\"\n", acpi_facp_oem_id);
        printf(" : first structure address = %x\n", acpi_rsdp.rsdt_address + 36);
        printf(" : structure count = %u\n", ((acpi_rsdt.length - 36) / 4));
    }

    printf("\nhardware info:\n");
    printf(" : CPU: \n");
    
    if(cpuid.cpu_brand[0])
    {
        for(c = 0; c < 49; c++)
        {
            if(cpuid.cpu_brand[c] != ' ')
            {
                printf("   : brand = %s\n", (uint8_t*)(cpuid.cpu_brand + c));
                break;
            }
        }
    }
    
    if(sm_cpu[0].manufacturer_s){
    printf("   : manufacturer = %s\n", sm_cpu[0].manufacturer_s);}
    if(sm_cpu[0].serial_s){
    printf("   : serial = %s\n", sm_cpu[0].serial_s);}
    printf("   : count = %ux%u\n", max((uint32_t)sm_socket_count, 1), max(mp.processor_count, sm_cpu_count) / max((uint32_t)sm_socket_count, 1));
    printf("   : features = %x:%x\n", cpuid.features_edx, cpuid.features_ecx);
    
    if(sm_cpu[0].current_speed){
    printf("   : speed = %uMHz\n", sm_cpu[0].current_speed);}
    printf(" : RAM: \n");
    printf("   : GRUB size = %uMB\n", ((grub_global_mbi->mem_upper + 1024) / 1024));
    printf("   : SMBIOS size = %uMB\n", get_smbios_memory_count());
    if(sm_memdev[0].speed){
    printf("   : speed = %uMHz\n", sm_memdev[0].speed);}
    printf("   : slots = %u used, %u total\n", sm_memdev_filled, sm_memdev_count);

    printf("\ntask info:\n");
    printf(" : active tasks = %u\n", active_task_count);
    printf(" : task list:\n");

    for(x = 0; x < 32; x++)
    {
        if(task[x].id > 0)
        {printf("   : [#%u] \"%s\"\n", task[x].id, task[x].name);}
    }
    
    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void memory_info(void)
{
    uint32_t x, i;
    
    GUTS();
    
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");
    
    printf("CPU cache information:\n");
    printf(" : L2 cache size = %uKB\n", cpuid.l2_cache_size);
    printf(" : L2 cache line size = %uKB\n", cpuid.l2_cache_line_size);
    
    if(cpuid.cache_count){
    printf("\nCPUID cache information:\n");}
    
    for(i = 0; i < cpuid.cache_count; i++)
    {
        printf(" : L%u:\n", cpuid.cache_info->level);
        printf("   : type = %x\n", cpuid.cache_info->type); 
        printf("   : size = %u bytes\n", cpuid.cache_info->cache_size); 
    }
    
    struct multiboot_memory_map * mbmm;
    if(grub_global_mbi->flags & (1<<6))
    {
        printf("\nGRUB memory map:\n");
        for(i = 0; i < grub_global_mbi->mmap_length; i += (mbmm->size + 4))
        {
            mbmm = (struct multiboot_memory_map*)(grub_global_mbi->mmap_address + i);

            printf(" : %x-%x is %s \n", mbmm->base_address_low, mbmm->base_address_low + mbmm->length_low, mbmm_types[mbmm->type]);
        }
    }
    
    printf("\nMemory device information:\n");
    for(i = 0; i < sm_memdev_count; i++)
    {
        if(sm_memdev[i].size)
        {
            printf(" : RAM slot #%u:\n", i);
            if(sm_memdev[i].form_factor <= 0x0F){
            printf("   : slot type = \"%s\"\n", sm_memory_slots[sm_memdev[i].form_factor]);}
            if(sm_memdev[i].type <= 0x14){
            printf("   : device type = \"%s\"\n", sm_memory_types[sm_memdev[i].type]);}
            printf("   : status = %s\n", "occupied");
            printf("   : size = %uMB\n", sm_memdev[i].size);
        }
        else
        {
            printf(" : RAM slot #%u:\n", i);
            if(sm_memdev[i].form_factor <= 0x0F){
            printf("   : slot type = \"%s\"\n", sm_memory_slots[sm_memdev[i].form_factor]);}
            if(sm_memdev[i].type <= 0x14){
            printf("   : device type = \"%s\"\n", sm_memory_types[sm_memdev[i].type]);}
            printf("   : status = %s\n", "empty");
        }
        printf("\n");
    }
    
    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void port_info(void)
{
    uint32_t x;
    
    GUTS();
      
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");
    
    if(!sm_port_count)
    {
        printf("SMBIOS did not provide any port information.");
    }
    
    for(x = 0; x < sm_port_count; x++)
    {
        printf("port #%u:\n", x);
        
        if(sm_port[x].port_type <= 0x21)
        {printf(" : type = \"%s\"\n", sm_ports[sm_port[x].port_type]);}
        else if(sm_port[x].port_type >= 0xA0 && sm_port[x].port_type <= 0xA1)
        {printf(" : type = \"%s\"\n", sm_ports[sm_port[x].port_type - (0xA0 - 0x22)]);}
        else{printf(" : type = UNKNOWN\n");}

        if(sm_port[x].internal_connector_type <= 0x22)
        {printf(" : internal connector = \"%s\"\n", sm_connectors[sm_port[x].internal_connector_type]);}
        else if(sm_port[x].internal_connector_type >= 0xA0 && sm_port[x].internal_connector_type <= 0xA4)
        {printf(" : internal connector = \"%s\"\n", sm_connectors[sm_port[x].internal_connector_type - (0xA0 - 0x23)]);}
        else{printf(" : internal connector = UNKNOWN\n");}
                
        if(sm_port[x].external_connector_type <= 0x22)
        {printf(" : external connector = \"%s\"\n", sm_connectors[sm_port[x].external_connector_type]);}
        else if(sm_port[x].external_connector_type >= 0xA0 && sm_port[x].external_connector_type <= 0xA4)
        {printf(" : external connector = \"%s\"\n", sm_connectors[sm_port[x].external_connector_type - (0xA0 - 0x23)]);}
        else{printf(" : external connector = UNKNOWN\n");}
        
        printf("\n");
    }
    
    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void pci_info(void)
{
    uint32_t x;
    
    GUTS();
      
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");

    printf("basic PCI information: \n");
    printf(" : BIOS32 entry point = %x\n", Bios32_Entry_Point);
    printf(" : device count = %u\n", pci_devices);
    
    printf("\nPCI device information: \n");
    for(x = 0; x < pci_devices; x++)
    {
        printf(" : id #%u:\n", x);
        printf("   : vendor = %x (\"%s\")\n", pci_device[x].vendor_id, PciVenTable[find_pci_vendor(pci_device[x].vendor_id)].VenShort);
        printf("   : device = %x (\"%s\")\n", pci_device[x].device_id, PciDevTable[find_pci_device(pci_device[x].vendor_id, pci_device[x].device_id)].ChipDesc);
        printf("\n");
    }

    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void slot_info(void)
{
    uint32_t x;
    
    GUTS();
      
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");

    if(!sm_slot_count)
    {
        printf("SMBIOS did not provide any slot information.");
    }
    
    for(x = 0; x < sm_slot_count; x++)
    {
        printf("port #%u\n", x);
        
        if(sm_slot[x].type <= 0x13)
        {printf(" : type = %s\n", sm_slot_types[sm_slot[x].type]);}
        else if(sm_slot[x].type >= 0xA0 && sm_slot[x].type <= 0xAA)
        {printf(" : type = %s\n", sm_slot_types[sm_slot[x].type - 0x14]);}
        else{printf(" : type = UNKNOWN (%x)\n", sm_slot[x].type);}
        
        printf(" : designation = \"%s\"\n", sm_slot[x].designation_s);
    
        printf("\n");
    }

    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void smbios_info(void)
{
    uint32_t x, i;
    
    GUTS();
      
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");

    printf("basic SMBIOS information: \n");
    printf(" : version = %u.%u\n", sm_eps.major_version, sm_eps.minor_version);
    printf(" : first structure address = %x\n", sm_eps.struct_table_address); 
    printf(" : structure count = %u\n", sm_structure_count);
    printf(" : structure listings: \n");
    printf("   : ");
    for(i = 0; i < sm_structure_count; i++)
    {
        if(!(i%10) && i>0)
        {printf("\n   : ");}
        
        printf("%x ", sm_structures[i].type);
    }

    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

static void acpi_info(void)
{
    uint32_t x, i;
    
    GUTS();
      
    cls();
    start_ui_more_monitor("Press 'Space' to scroll one line or 'Enter' to print everything.");

    printf("basic ACPI information: \n");
    printf(" : version = %u\n", acpi_rsdp.revision+1);
    printf(" : APIC:\n");
    printf("   : LAPIC count = %u\n", acpi_lapic_count);
    printf("   : IO-APIC count = %u\n", acpi_ioapic_count);
    printf("   : Interrupt Source Override count = %u\n", acpi_iso_count);
    printf("   : Non-Maskable Interrupt count = %u\n", acpi_nmi_count);
    printf("   : LAPIC Non-Maskable Interrupt count = %u\n", acpi_lapic_nmi_count);
    printf("   : LAPIC Address Override count = %u\n", acpi_lapic_ao_count);
    printf("   : IO-SAPIC count = %u\n", acpi_iosapic_count);
    ui_monitor = false;
    info_bar_printf("Press 'q' to return");
    wait_for_key('q');
}

void init_standard_commands(void)
{   
    while(!smbios_parsing_completed || 
          !acpi_parsing_completed)
    {GUTS();}
    
    add_shell_command("help", help);
    add_shell_command("cls", cls);
    add_shell_command("system-info", system_info);
    add_shell_command("port-info", port_info);
    add_shell_command("memory-info", memory_info);
    add_shell_command("pci-info", pci_info);
    add_shell_command("slot-info", slot_info);
    add_shell_command("smbios-info", smbios_info);
    add_shell_command("acpi-info", acpi_info);
}

